/**
 * Alipay.com Inc.
 * Copyright (c) 2005-2008 All Rights Reserved.
 */
package com.probiz.estoremf.payment.alipay.trade;

import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.StringUtils;
import org.springframework.web.servlet.ModelAndView;

import com.probiz.estore.common.helper.ConfigUtil;
import com.probiz.estore.common.model.system.PaymentGateway;
import com.probiz.estore.common.model.system.PaymentMethod;
import com.probiz.estore.core.controller.GenericStoreFrontController;
import com.probiz.estore.system.service.PaymentMethodManager;
import com.probiz.estoremf.payment.alipay.base.ResponseResult;
import com.probiz.estoremf.payment.alipay.base.TradeConfig;
import com.probiz.estoremf.payment.alipay.security.MD5Signature;
import com.probiz.estoremf.payment.alipay.util.ParameterUtil;
import com.probiz.estoremf.payment.alipay.util.StringUtil;
import com.probiz.estoremf.payment.alipay.util.XMapUtil;
import com.probiz.estoremf.payment.alipay.vo.DirectTradeCreateRes;
import com.probiz.estoremf.payment.alipay.vo.ErrorCode;

/**
 * 
 * 调用支付宝的开放平台创建、支付交易步骤
 * 
 * 1.将业务参数：外部交易号、商品名称、商品总价、卖家帐户、卖家帐户、notify_url这些东西按照xml
 * 的格式放入<req_data></req_data>中 2.将通用参数也放入请求参数中 3.对以上的参数进行签名，签名结果也放入请求参数中
 * 4.请求支付宝开放平台的alipay.wap.trade.create.direct服务
 * 5.从开放平台返回的内容中取出request_token（对返回的内容要先用私钥解密，再用支付宝的公钥验签名）
 * 6.使用拿到的request_token组装alipay.wap.auth.authAndExecute服务的跳转url
 * 7.根据组装出来的url跳转到支付宝的开放平台页面，交易创建和支付在支付宝的页面上完成
 * 
 * @author 3y
 * @version $Id: Trade.java, v 0.1 2011-08-22 下午17:52:02 3y Exp $
 */
public class TradeController extends  GenericStoreFrontController {

	private static final long serialVersionUID = -3035307235076650766L;
	/** 签名类型 MD5 */
	private static final String SEC_ID = "MD5";
	
	private PaymentMethodManager paymentMethodManager = null ; 

	public void setPaymentMethodManager(PaymentMethodManager paymentMethodManager) {
		this.paymentMethodManager = paymentMethodManager;
	}

	/**
	 * 用户请求创建支付交易信息
	 * @param request
	 * @param response
	 */
	public ModelAndView defaultAction(HttpServletRequest request, HttpServletResponse response){
		response.setContentType("text/html;charset=UTF-8");
		
		try {
			/** 获取商户MD5 key */
			String key = "";
			String payId = request.getParameter("payId");
			if(StringUtils.isNotEmpty(payId)){
				PaymentMethod paymentMethod = this.paymentMethodManager.getById(Integer.valueOf(payId));
				if(paymentMethod != null){
					if (paymentMethod.getPaymentMethodType().equals(PaymentMethod.PAYMENT_METHOD_TYPE_ONLINE)) {// 判断是线上支付还是线下支付
						PaymentGateway paymentGateway = paymentMethod.getPaymentGateway();
						if (paymentGateway != null) {
							HashMap paramMap = paymentGateway.getConfigData();
							key = paramMap.get("key").toString().trim();
							request.setAttribute("configData", paramMap);
						}
					}
				}
			}
			
			Map<String, String> reqParams = prepareTradeRequestParamsMap(request);
			String signAlgo = SEC_ID;
			String reqUrl = TradeConfig.REQ_URL;
			
			String sign = sign(reqParams, signAlgo, key);
			reqParams.put("sign", sign);
	
			ResponseResult resResult = new ResponseResult();
			String businessResult = "";
			try {
				resResult = send(reqParams, reqUrl, key);
			} catch (Exception e1) {
				e1.printStackTrace();
			}
			if (resResult.isSuccess()) {
				businessResult = resResult.getBusinessResult();
			} else {
				PrintWriter out = response.getWriter();
				out.print("出错信息：" + resResult.getErrorMessage().getDetail());
				out.flush();
				System.out.println("出错信息："
						+ resResult.getErrorMessage().getDetail());
				return null;
			}
			DirectTradeCreateRes directTradeCreateRes = null;
			XMapUtil.register(DirectTradeCreateRes.class);
			try {
				directTradeCreateRes = (DirectTradeCreateRes) XMapUtil
						.load(new ByteArrayInputStream(businessResult
								.getBytes("UTF-8")));
			} catch (UnsupportedEncodingException e) {
			} catch (Exception e) {
			}
			/** 开放平台返回的内容中取出request_token */
			String requestToken = directTradeCreateRes.getRequestToken();
			Map<String, String> authParams = prepareAuthParamsMap(request,
					requestToken);
			/** 对调用授权请求数据签名 */
			String authSign = sign(authParams, signAlgo, key);
			authParams.put("sign", authSign);
			String redirectURL = "";
			try {
				redirectURL = getRedirectUrl(authParams, reqUrl);
				//System.out.println("redirectURL:" + redirectURL);
			} catch (Exception e) {
				e.printStackTrace();
			}
			if (StringUtil.isNotBlank(redirectURL)) {
				response.sendRedirect(redirectURL);
				return null;
			}
		} catch (UnsupportedEncodingException e2) {
			e2.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 准备alipay.wap.trade.create.direct服务的参数
	 * 
	 * @param request
	 * @return 请求参数map
	 * @throws UnsupportedEncodingException
	 */
	private Map<String, String> prepareTradeRequestParamsMap(
			HttpServletRequest request) throws UnsupportedEncodingException {
		Map<String, String> requestParams = new HashMap<String, String>();
		String cashierCode=request.getParameter("cashierCode");
		String out_user=(String)request.getSession().getAttribute("out_user");
		if(StringUtil.isBlank(out_user)){
            out_user="";
        }

		request.setCharacterEncoding("utf-8");
		/**商品名称*/
		String subject = request.getParameter("subject");
		/**商品总价*/
        String totalFee = request.getParameter("price");
        /**外部交易号 这里取当前时间，商户可根据自己的情况修改此参数，但保证唯一性*/
        String outTradeNo = request.getParameter("outTradeNo");

		/**卖家帐号*/
        String sellerAccountName ="";
		HashMap paramMap = (HashMap) request.getAttribute("configData");
        if(paramMap != null){
        	sellerAccountName= paramMap.get("merchant").toString().trim();
        }
        
		
		
		/**接收支付宝发送的通知的url*/
        StringBuilder notifyUrl = new StringBuilder();
        notifyUrl.append(ConfigUtil.getInstance().getMobileFrontSiteUrl()).append("/system/payment/alipay_wap_response.html");
		
		/**支付成功跳转链接*/
        StringBuilder callbackUrl = new StringBuilder();
        callbackUrl.append(ConfigUtil.getInstance().getMobileFrontSiteUrl()).append("/customer/csOrders.html");
        
		/**未完成支付，用户点击链接返回商户url*/
		String merchantUrl = ConfigUtil.getInstance().getMobileFrontSiteUrl();
		
		
		
		
		
		/**req_data的内容*/
		StringBuilder reqData = new StringBuilder();
		reqData.append("<direct_trade_create_req>").append("<subject>"+ subject+"</subject>");
		reqData.append("<out_trade_no>" + outTradeNo+ "</out_trade_no>");
		reqData.append("<total_fee>" + totalFee+ "</total_fee>");
		reqData.append("<seller_account_name>" + sellerAccountName+ "</seller_account_name>");
		reqData.append("<notify_url>" + notifyUrl.toString()+ "</notify_url>");
		reqData.append("<call_back_url>" + callbackUrl.toString()+ "</call_back_url>");
		reqData.append("<out_user>" + out_user+ "</out_user>");
		reqData.append("<merchant_url>" + merchantUrl+ "</merchant_url>");
		/**如果cashierCode不为空就组装此参数*/
        if (StringUtil.isNotBlank(cashierCode)) {
        	reqData.append("<cashier_code>" + cashierCode+ "</cashier_code>");
        }
		reqData.append("</direct_trade_create_req>");
		requestParams.put("req_data", reqData.toString());
		requestParams.put("req_id", System.currentTimeMillis() + "");
		requestParams.putAll(prepareCommonParams(request));
		return requestParams;
	}

	/**
	 * 准备alipay.wap.auth.authAndExecute服务的参数
	 * 
	 * @param request
	 * @param requestToken
	 * @return 返回授权接口参数map
	 */
	private Map<String, String> prepareAuthParamsMap(
			HttpServletRequest request, String requestToken) {
		Map<String, String> requestParams = new HashMap<String, String>();
		StringBuilder reqData = new StringBuilder();
		reqData.append("<auth_and_execute_req><request_token>"+requestToken+"</request_token>")
				.append("</auth_and_execute_req>");
		requestParams.put("req_data", reqData.toString());
		requestParams.putAll(prepareCommonParams(request));
		requestParams.put("service", "alipay.wap.auth.authAndExecute");
		return requestParams;
	}

	/**
	 * 准备通用参数
	 * 
	 * @param request
	 * @return 通用参数map
	 */
	private Map<String, String> prepareCommonParams(HttpServletRequest request) {
		Map<String, String> commonParams = new HashMap<String, String>();
		commonParams.put("service", "alipay.wap.trade.create.direct");
		commonParams.put("sec_id", SEC_ID);
		String partner = "";
		HashMap paramMap = (HashMap) request.getAttribute("configData");
        if(paramMap != null){
        	partner= paramMap.get("partnerId").toString().trim();
        }
		commonParams.put("partner", partner);
		commonParams.put("format", "xml");
		commonParams.put("v", "2.0");
		return commonParams;
	}

	/**
	 * 对参数进行签名
	 * @param reqParams 待签名参数
	 * @param signAlgo 签名类型
	 * @param key MD5校验码
	 * @return 返回签名结果
	 */
	private String sign(Map<String, String> reqParams, String signAlgo,
			String key) {

		String signData = ParameterUtil.getSignData(reqParams);

		String sign = "";
		try {
			sign = MD5Signature.sign(signData, key);
		} catch (Exception e1) {
			e1.printStackTrace();
		}
		return sign;
	}

	/**
	 * 调用alipay.wap.auth.authAndExecute服务的时候需要跳转到支付宝的页面，组装跳转url
	 * @param reqParams
	 * @param reqUrl 请求url
	 * @return 返回组装好的url字符串
	 * @throws Exception
	 */
	private String getRedirectUrl(Map<String, String> reqParams, String reqUrl)
			throws Exception {
		String redirectUrl = reqUrl + "?";
		redirectUrl = redirectUrl + ParameterUtil.mapToUrl(reqParams);
		return redirectUrl;
	}

	/**
	 * 获取请求数据
	 * @param reqParams 请求参数map
	 * @param reqUrl 请求url
	 * @return 返回请求结果数据
	 * @throws Exception
	 */
	private ResponseResult send(Map<String, String> reqParams, String reqUrl, String key) throws Exception {
		String response = "";
		String invokeUrl = reqUrl;
		URL serverUrl = new URL(invokeUrl);
		HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection();

		conn.setRequestMethod("POST");
		conn.setDoOutput(true);
		conn.connect();
		String params = ParameterUtil.mapToUrl(reqParams);
		conn.getOutputStream().write(params.getBytes());

		InputStream is = conn.getInputStream();

		BufferedReader in = new BufferedReader(new InputStreamReader(is));
		StringBuffer buffer = new StringBuffer();
		String line = "";
		while ((line = in.readLine()) != null) {
			buffer.append(line);
		}
		response = URLDecoder.decode(buffer.toString(), "utf-8");
		conn.disconnect();
		return praseResult(response, key);
	}

	/**
	 * 解析支付宝返回的结果
	 * @param response
	 * @return
	 * @throws Exception
	 */
	private ResponseResult praseResult(String response, String key)
			throws Exception {
		HashMap<String, String> resMap = new HashMap<String, String>();
		String v = ParameterUtil.getParameter(response, "v");
		String service = ParameterUtil.getParameter(response, "service");
		String partner = ParameterUtil.getParameter(response, "partner");
		String sign = ParameterUtil.getParameter(response, "sign");
		String reqId = ParameterUtil.getParameter(response, "req_id");
		resMap.put("v", v);
		resMap.put("service", service);
		resMap.put("partner", partner);
		resMap.put("sec_id", SEC_ID);
		resMap.put("req_id", reqId);
		String businessResult = "";
		ResponseResult result = new ResponseResult();
		if (response.contains("<err>")) {
			result.setSuccess(false);
			businessResult = ParameterUtil.getParameter(response, "res_error");
			/** 转换错误信息 */
			XMapUtil.register(ErrorCode.class);
			ErrorCode errorCode = (ErrorCode) XMapUtil
					.load(new ByteArrayInputStream(businessResult
							.getBytes("UTF-8")));
			result.setErrorMessage(errorCode);
		} else {
			businessResult = ParameterUtil.getParameter(response, "res_data");
			result.setSuccess(true);
			result.setBusinessResult(businessResult);
			resMap.put("res_data", businessResult);
			/** 获取待签名数据 */
			String verifyData = ParameterUtil.getSignData(resMap);
			/** 对待签名数据验签名 */
			boolean verified = MD5Signature.verify(verifyData, sign, key);
			if (!verified) {
				throw new Exception("验证签名失败");
			}
		}

		return result;
	}

	@Override
	protected void initController() throws Exception {
		// TODO Auto-generated method stub
		
	}
}
